#pragma once

#include <string>
#include <vector>
#include <fstream>
#include <list>
#include <map>
#include <iostream>

using std::string;
using std::vector;
using std::map;

// Частотный словарь. В качестве частоты используется количество вхождений в текст

typedef std::map<string, size_t> TFreqDictPart;
typedef std::map<size_t, size_t> TFreqCounter;

typedef struct
{
	TFreqDictPart dict;
	TFreqCounter r;
} TFreqDict;


struct TPhraseMatching {
    TFreqDict RuPhraseFreq; // сколько раз фразе на русском был сопоставлен перевод (любой)
    TFreqDict EnPhraseFreq; // сколько раз фразе на английском был сопоставлен перевод (любой)
    map<string, TFreqDict> Translations; // для фразы на русском - все ее переводы на английский (с частотами)
};

// Считать модель перевода из файла.
// Формат файла: каждая строка имеет вид "phrase_en ||| phrase_ru ||| freq_en ||| freq_ru ||| freq
// Где phrase_ru/en - сама фраза, в которой отдельные слова разделены одним пробелом,
// freq_en/ru - соответствуют полям TPhraseMatching.En/RuPhraseFreq
// freq - сколько раз phrase_en и phrase_ru были сопоставлены друг другу. Соответствует TPhraseMatching.Translations.second
std::istream& operator>>(std::istream& in, TPhraseMatching& phraseMatching);
std::ostream& operator<<(std::ostream& out, const TPhraseMatching& phraseMatching);

// Необходимые для построения модели перевода файлы.
struct TFileNames {
    string RawNameRu; // Файл с текстом на русском.
    string GizaNameRuEn; // Файл с выравниванием один токен на английском --> токен(ы) на русском.
    string RawNameEn; // Файл с текстом на английском.
    string GizaNameEnRu; // Файл с выравниванием один токен на русском --> токен(ы) на английском.
    string OutName; // Результирующий файл, в который будет записана модель перевода.
    TFileNames(string rawru, string gizare, string rawen, string gizaer, string out)
        : RawNameRu(rawru)
        , GizaNameRuEn(gizare)
        , RawNameEn(rawen)
        , GizaNameEnRu(gizaer)
        , OutName(out)
    {}
};

// две фразы, соответствующие друг другу
struct TAlignedPhrase {
    string EnPhrase;
    string RuPhrase;
};

// информация о двух выровненных относительно друг друга фразах
struct TAlignedPhraseInfo {
    size_t EnCount; // длина фразы на английском в токенах
    size_t RuCount; // длина фразы на русском в токенах
    size_t MatchCount; // число сопоставленных токенов
    TAlignedPhraseInfo(size_t enCount, size_t ruCount, size_t matchCount)
        : EnCount(enCount)
        , RuCount(ruCount)
        , MatchCount(matchCount)
    {}
};

// узел предложения - одно слово
class TRawNode {
public:
    string Form; // форма слова
    int WordId; // идентификатор слова

public:
    TRawNode()
        : WordId(-1)
    {}

    TRawNode(const int id, const string& text)
        : Form(text)
        , WordId(id)
    {}
};

// одно предложение в текстовом формате
class TRawSent {
public:
    string Text;
    int SentId;
    vector<TRawNode> Words;
    vector<int> GizaPoints; // массив для перехода к выровненным словам в параллельном предложении
};

// класс для обработки пары параллельных предложений
class TPhraseExtractor {

private:
    vector< vector<int> > Matrix; // матрица из CellAlignment
    const TRawSent *SentRu;
    const TRawSent *SentEn;

public:
    enum CellAlignment { // соотношение двух слов: одного на русском, одного на английском в матрице выравниваний
        None = 0,
        RuEn = 0x01, // Есть в отображении английского токена на русские
        EnRu = 0x02, // Есть в отображении русского токена на английские
        Both = 0x03 // RuEn & EnRu
    };

    TPhraseExtractor(const TRawSent *sentRu, const TRawSent *sentEn)
        : SentRu(sentRu)
        , SentEn(sentEn)
    {}
    void InitAlignment(); // заполнить матрицу
    void AddNewAlignmentPoints(); // сейчас не используется, можно добавить мозесный grow-diag-final 
    void DebugPrintAlignment(std::ostream& out) const; // распечатать матрицу
    std::list<TAlignedPhrase> ExtractPhrases(size_t maxPhraseLength) const; // извлечь фразы, не противоречащие пословному выравниванию
};

// класс для чтения предложений в текстовом формате
class TRawReader {
public:
    std::ifstream Input;
    std::ifstream GizaInput;
    // результат работы последнего вызова ReadNSent - прочитанные предложения
    std::list<TRawSent> AllSent;

public:
    TRawReader() {}
    // попытаться считать n предложений из потока, возвратить число реально прочитанных предложений
    // очищает и пишет результат в поле AllSent
    size_t ReadNSent(size_t n);
    void DebugPrintAllSent(std::ostream& out) const;
    
    void Open(const string& filename, const string& gizaFilename);
    void Close();

private:
    int NextSentId;
private:
    // вспомогательная функция читает предложение из потока и добавляет соответствующую конструкцию в хвост списка AllSent
    bool ReadNextSent();
};

// main entry point
void ProcessFiles(const TFileNames& filenames, TRawReader& rawReaderRu, TRawReader& rawReaderEn);
